/*
 * This file is part of FinVis.
 *
 * FinVis is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FinVis is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with FinVis.  If not, see <http://www.gnu.org/licenses/>
 *
 * @author Stephen Rudolph
 */

#include <QtGui>
#include <QMenuBar>
#include <QListWidget>
#include <QBoxLayout>
#include <QToolBar>
#include <QMessageBox>
#include <QApplication>

#include <vector>

#include "Common.h"
#include "FinException.h"
#include "FinInvestmentCommands.h"
#include "FinInvestmentWidget.h"
#include "FinList.h"
#include "FinPortfolio.h"
#include "FinRecorder.h"
#include "FinScenario.h"
#include "FinSetupDialog.h"
#include "FinSettings.h"
#include "FinView.h"
#include "FinViewKey.h"
#include "FinWindow.h"

FinPortfolio scenarioPortfolio;
FinRecorder recorder;
FinScenario scenario;
std::vector<InvestmentOption> investmentOptionVector;
enum FinView::display displaySetting;

FinWindow::FinWindow()
{
    InvestmentOption option;

    //Load investment data
    option.annualPercentageReturn = 0.0;
    option.standardDeviation = 0.0;
    option.durationYears = 1;
    option.simpleInterest = false;
    option.name[0] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 3;
    option.standardDeviation = 3;
    option.name[0] = 'A';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 5;
    option.standardDeviation = 6;
    option.name[0] = 'B';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 7;
    option.standardDeviation = 12;
    option.name[0] = 'C';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 3;
    option.standardDeviation = 3;
    option.name[0] = 'D';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 5;
    option.standardDeviation = 6;
    option.name[0] = 'E';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 5;
    option.standardDeviation = 6;
    option.name[0] = 'F';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    option.annualPercentageReturn = 2;
    option.standardDeviation = 0.0;
    option.name[0] = 'G';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);
    scenario.initialize();
    /*option.annualPercentageReturn = 2;
    option.standardDeviation = 0.0;
    option.name[0] = 'D';
    option.name[1] = '\0';
    investmentOptionVector.push_back(option);*/

    this->checkForExistingDatabase();

    FinSettings *settings = new FinSettings();
    displaySetting = settings->getDisplayType();
    switch (displaySetting)
    {
        case FinView::DEFAULT_VIEW:
            mainView = new FinView(FinView::DEFAULT_VIEW, this);
            miniView = new FinView(FinView::MINI_VIEW, this);
            break;
        case FinView::SIMPLE_VIEW:
            mainView = new FinView(FinView::SIMPLE_VIEW, this);
            miniView = new FinView(FinView::MINI_VIEW, this);
            break;
        case FinView::TABLE_VIEW:
            mainView = new FinView(FinView::TABLE_VIEW, this);
            miniView = new FinView(FinView::MINI_VIEW, this);
            break;
        case FinView::OPTIMISTIC_ONLY:
            mainView = new FinView(FinView::OPTIMISTIC_ONLY, this);
            miniView = new FinView(FinView::MINI_VIEW_OPTIMISTIC_ONLY, this);
            break;
        case FinView::PESSIMISTIC_ONLY:
            mainView = new FinView(FinView::PESSIMISTIC_ONLY, this);
            miniView = new FinView(FinView::MINI_VIEW_PESSIMISTIC_ONLY, this);
            break;
        default:
            mainView = new FinView(FinView::DEFAULT_VIEW, this);
            miniView = new FinView(FinView::MINI_VIEW, this);
            break;
    }

    //mainView = new FinView(FinView::TABLE_VIEW, this);
    //liquidAssetView = new FinView(FinView::LIQUID_ASSETS, this);

    mainList = new FinList(this);
    connect(mainList, SIGNAL(addAndSelectInvestment()),
            this, SLOT(addAndSelectInvestment()));
    connect(mainList, SIGNAL(removeSelectedInvestment()),
            this, SLOT(removeSelectedInvestment()));
    connect(&scenarioPortfolio, SIGNAL(portfolioDataChanged()),
            mainList, SLOT(portfolioDataChanged()));
    connect(&scenarioPortfolio, SIGNAL(selectionChanged()),
            mainList, SLOT(selectionChanged()));
    connect(mainList, SIGNAL(selectionChanged(unsigned int)),
            &scenarioPortfolio, SLOT(setSelectedID(unsigned int)));

    /*QWidget *plotWiget = new QWidget(this);
    QVBoxLayout *plotVBoxLayout = new QVBoxLayout;
    plotVBoxLayout->addWidget(netWealthView);
    plotVBoxLayout->addWidget(liquidAssetView);
    plotWiget->setLayout(plotVBoxLayout);
    */

    investmentWidget = new FinInvestmentWidget(this);
    connect(investmentWidget, SIGNAL(investmentChanged(FinInvestment)),
           this, SLOT(investmentChanged(FinInvestment)));

    viewKey = new FinViewKey(this);
    connect(viewKey, SIGNAL(symbolDisabled(::symbols)),
            mainView, SLOT(disableSymbol(::symbols)));
    connect(viewKey, SIGNAL(symbolEnabled(::symbols)),
            mainView, SLOT(enableSymbol(::symbols)));
    //mainView->setFixedSize(mainView->sizeHint());
    
    mainView->setMinimumWidth(300);
    setCentralWidget(mainView);
    
    //mainList->setFixedSize(mainList->sizeHint());
    //investmentWidget->setFixedSize(investmentWidget->sizeHint());
    
    miniDock = new QDockWidget(tr("Overview"));
    miniDock->setWidget(miniView);
    miniDock->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::RightDockWidgetArea, miniDock);
    viewKey->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::RightDockWidgetArea, viewKey);
    splitDockWidget(miniDock, viewKey, Qt::Vertical);
    miniDock->setHidden(displaySetting == FinView::TABLE_VIEW);
    viewKey->setHidden(displaySetting == FinView::TABLE_VIEW);

    investmentWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::BottomDockWidgetArea, investmentWidget);
    mainList->setFeatures(QDockWidget::NoDockWidgetFeatures);
    addDockWidget(Qt::BottomDockWidgetArea, mainList);
    splitDockWidget(mainList, investmentWidget, Qt::Horizontal);

    setWindowTitle(tr("FinVis"));

    //addInvestmentAction = new QAction(QIcon(":/Add.png"), tr("Add"), this);
    //removeInvestmentAction = new QAction(QIcon(":/Remove.png"), tr("Remove"), this);
    //editInvestmentAction = new QAction(QIcon(":/Edit.png"), tr("Edit"), this);
    //showInvestmentListAction = new QAction(tr("Show Investment List"), this);
    //showInvestmentListAction->setCheckable(true);
    //showInvestmentListAction->setChecked(true);
    zoomInAction = new QAction(QIcon(":/plus.png"), tr("Zoom In"), this);
    zoomOutAction = new QAction(QIcon(":/minus.png"), tr("Zoom Out"), this);
    zoomInAction->setEnabled(displaySetting != FinView::TABLE_VIEW);
    zoomOutAction->setEnabled(displaySetting != FinView::TABLE_VIEW);

    //sampleAction = new QAction(QIcon(":/sample.png"), tr("Sample Outcome"), this);
    //sampleAction->setEnabled(false);

    playAction = new QAction(QIcon(":/start.png"), tr("Start"), this);
    stopAction = new QAction(QIcon(":/stop.png"), tr("Finish"), this);

    if (scenario.isScenarioStarted())
    {
        playAction->setEnabled(false);
    }
    else
    {
        stopAction->setEnabled(false);
    }

    if (!scenario.isFirstScenario())
    {
        playAction->setIcon(QIcon(":/next.png"));
        playAction->setText(tr("Next"));
    }

    //undoStack = new QUndoStack();
    //undoAction = undoStack->createUndoAction(this, tr("&Undo"));
    //QIcon undoIcon = QIcon::fromTheme("edit-undo", QIcon(":/undo.png"));
    //QIcon redoIcon = QIcon::fromTheme("edit-redo", QIcon(":/redo.png"));
    //undoAction->setIcon(undoIcon);
    //undoAction->setShortcut(QKeySequence::Undo);
    //redoAction = undoStack->createRedoAction(this, tr("&Redo"));
    //redoAction->setShortcut(QKeySequence::Redo);
    //redoAction->setIcon(redoIcon);

    //connect(addInvestmentAction, SIGNAL(triggered()), this, SLOT(addInvestment()));
    //connect(removeInvestmentAction, SIGNAL(triggered()), this, SLOT(removeInvestment()));
    //connect(editInvestmentAction, SIGNAL(triggered()), this, SLOT(editInvestment()));
    //connect(showInvestmentListAction, SIGNAL(triggered()), this, SLOT(showInvestmentList()));
    //connect(mainList->investmentVectorView, SIGNAL(currentRowChanged(int)), this, SLOT(changeRow(int)));
    connect(zoomInAction, SIGNAL(triggered()), mainView, SLOT(decreasePlotBounds()));
    connect(zoomOutAction, SIGNAL(triggered()), mainView, SLOT(increasePlotBounds()));
    //connect(sampleAction, SIGNAL(triggered()), mainView, SLOT(drawSampleOutcome()));
    connect(playAction, SIGNAL(triggered()), this, SLOT(startScenario()));
    connect(stopAction, SIGNAL(triggered()), this, SLOT(stopScenario()));

    controlToolBar = addToolBar(tr("Control"));
    controlToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    controlToolBar->addAction(playAction);
    controlToolBar->addAction(stopAction);
    //controlToolBar->addSeparator();
    //controlToolBar->addAction(undoAction);
    //controlToolBar->addAction(redoAction);

    viewToolBar = addToolBar(tr("View"));
    viewToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    viewToolBar->addAction(zoomInAction);
    viewToolBar->addAction(zoomOutAction);
    //viewToolBar->addAction(sampleAction);

    QMenuBar *menu = new QMenuBar();
    QMenu *view = new QMenu();
    view->setTitle(tr("View"));
    view->addAction(zoomInAction);
    view->addAction(zoomOutAction);
    //view->addAction(sampleAction);

    QMenu *control = new QMenu();
    control->setTitle(tr("Scenario"));
    control->addAction(playAction);
    control->addAction(stopAction);

    //QMenu *edit = new QMenu();
    //edit->setTitle(tr("Edit"));
    //edit->addAction(undoAction);
    //edit->addAction(redoAction);

    //menu->addMenu(edit);
    menu->addMenu(view);
    menu->addMenu(control);

    /*QMenu *edit = new QMenu();
    edit->setTitle(tr("Edit"));
    edit->addAction(addInvestmentAction);
    edit->addAction(removeInvestmentAction);
    //edit->addAction(editInvestmentAction);
    menu->addMenu(edit);*/

    //FinSetupDialog *fsd = new FinSetupDialog(&investmentVector);
    //fsd->exec();

    setWindowIcon(QIcon(":/logo.png"));

    evaluationTime.start();
}

FinWindow::~FinWindow()
{
}

void FinWindow::checkForExistingDatabase()
{
    if (recorder.hasHistory())
    {
        int sb = QMessageBox::question(
                    this,
                    "Progress Found",
                    "Would you like to open the progress file and continue or discard it and start over?",
                    QMessageBox::Open, QMessageBox::Discard);
        if (sb == QMessageBox::Discard)
        {
            recorder.clear();
        }
    }

    try
    {
        recorder.loadState();
    }
    catch (FinException)
    {
        QMessageBox::warning(this, "Database Error", "Could not open database.", QMessageBox::Ok);
    }
}

void FinWindow::startScenario()
{
    mainView->reset();
    miniView->reset();
    investmentWidget->reset();

    if (scenario.isFinished())
    {
        QMessageBox *done = new QMessageBox(QMessageBox::Warning, "Index Error", "Start error, currentScenario out of bounds\n");
        done->exec();
        delete done;

        QApplication::exit(0);
    }
    else
    {
        scenario.startScenario();
    }

    playAction->setEnabled(false);
    stopAction->setEnabled(true);
    //sampleAction->setEnabled(true);

    evaluationTime.restart();
    //this->redoAction->setEnabled(true);
    //this->undoAction->setEnabled(true);
    mainList->enableButtons();

    if (scenario.isFirstScenario())
    {
        recorder.recordAction("VizualizationType", displaySetting);
    }

    recorder.recordAction("BeginScenario", scenario.getCurrentScenario());
}

void FinWindow::stopScenario()
{
    stopAction->setEnabled(false);
    //sampleAction->setEnabled(false);
    playAction->setEnabled(true);
    int duration = evaluationTime.restart();
    recorder.recordAction("EndScenario", duration);

    //this->redoAction->setEnabled(false);
    //this->undoAction->setEnabled(false);
    mainList->disableButtons();
    investmentWidget->disableButtons();

    FinSetupDialog *confidenceAssesment = new FinSetupDialog();
    confidenceAssesment->exec();
    miniView->drawOutcome(mainView->drawFinalOutcome());

    scenario.stopScenario();
    if(scenario.isFinished())
    {
        QMessageBox *showSampleResult = new QMessageBox(QMessageBox::Information, "Finished!", tr("You are now finished. Thank you!"), QMessageBox::Ok);
        showSampleResult->exec();
        recorder.finishRecording();
        QApplication::exit(0);
    }

    playAction->setIcon(QIcon(":/next.png"));
    playAction->setText(tr("Next"));
}

void FinWindow::addAndSelectInvestment()
{
    try
    {
        scenarioPortfolio.addAndSelectInvestment();
    }
    catch (std::exception& e)
    {
        QMessageBox *msg = new QMessageBox(
                    QMessageBox::Critical, tr("Add Investment"),
                    tr("All funds have already been allocated."),
                    QMessageBox::Ok);
        msg->exec();
    }
    //AddInvestment *command = new AddInvestment(&scenarioPortfolio);
    //undoStack->push(command);
}

void FinWindow::removeSelectedInvestment()
{
    scenarioPortfolio.removeSelectedInvestment();
    //RemoveInvestment *command =
    //        new RemoveInvestment(&scenarioPortfolio,
    //                             scenarioPortfolio.selectedID());
    //undoStack->push(command);
}

void FinWindow::investmentChanged(FinInvestment investment)
{
    scenarioPortfolio.setSelectedInvestment(investment);
    recorder.recordAction("InvestmentChanged", 0);
    //ChangeInvestment *command = new ChangeInvestment(&scenarioPortfolio,
    //                                 scenarioPortfolio.selectedID(),
    //                                 investment);
    //undoStack->push(command);
}

void FinWindow::keyPressEvent(QKeyEvent *event)
{
    QWidget::keyPressEvent(event);
    //if(event->modifiers().testFlag(Qt::ShiftModifier))
    switch(event->key())
    {
        case Qt::Key_BraceRight:
        case Qt::Key_BracketRight:
        case Qt::Key_Right:
            scenarioPortfolio.setSelectedID(scenarioPortfolio.nextID());
            break;
        case Qt::Key_BraceLeft:
        case Qt::Key_BracketLeft:
        case Qt::Key_Left:
            scenarioPortfolio.setSelectedID(scenarioPortfolio.previousID());
            break;
        default:
            QWidget::keyPressEvent(event);
            break;
    }
}

